#include "raster.h"
#include "raster-triangles.h"

void __cdecl PrepareEdge(Edge *edge, const RasterVert *pt1, const RasterVert *pt2)
{
    Fixed invh = FIXED_ONE;
    Fixed sp;
    edge->h = FixedToInt(IABS(pt1->y - pt2->y));
    if(edge->h > 0)
    {
        invh /= edge->h;
    }

    if(pt1->y > pt2->y)
    {
        sp = FixedCeil(pt2->y) - pt2->y;
        edge->y = FixedToInt(pt2->y);
        edge->x = pt2->x;
        edge->dx = FixedMul(pt1->x - pt2->x, invh);
        edge->u = pt2->u;
        edge->du = FixedMul(pt1->u - pt2->u, invh);
        edge->v = pt2->v;
        edge->dv = FixedMul(pt1->v - pt2->v, invh);
        edge->w = pt2->w;
        edge->dw = FixedMul(pt1->w - pt2->w, invh);
    }
    else
    {
        sp = FixedCeil(pt1->y) - pt1->y;
        edge->y = FixedToInt(pt1->y);
        edge->x = pt1->x;
        edge->dx = FixedMul(pt2->x - pt1->x,  invh);
        edge->u = pt1->u;
        edge->du = FixedMul(pt2->u - pt1->u, invh);
        edge->v = pt1->v;
        edge->dv = FixedMul(pt2->v - pt1->v, invh);
        edge->w = pt1->w;
        edge->dw = FixedMul(pt2->w - pt1->w, invh);
    }
    /* subpixel correction */
    edge->x += FixedMul(edge->dx, sp);
    edge->u += FixedMul(edge->du, sp);
    edge->v += FixedMul(edge->dv, sp);
    edge->w += FixedMul(edge->dw, sp);
}

EXTERNC int __cdecl ClipSpan2(const RasterRect *clip, RasterSpan *span)
{
    Fixed invw;
    Fixed ustep, vstep, wstep;
    Fixed cx0 = IntToFixed(clip->x);
    Fixed cx1 = cx0 + IntToFixed(clip->w);
    int off;
    /* FIXME: should we draw spans less than a pixel wide? */
    if(span->x0 > cx1 || span->x1 < cx0 || FixedToInt(span->x1 - span->x0) <= 0)
    {
        span->x0 = 0;
        span->x1 = 0;
        return 0;
    }
    invw = FixedDiv(FIXED_ONE, span->x1 - span->x0);
    ustep = FixedMul(span->u1 - span->u0, invw);
    vstep = FixedMul(span->v1 - span->v0, invw);
    wstep = FixedMul(span->w1 - span->w0, invw);
    off = cx0 - span->x0;
    if(off > 0)
    {
        span->x0 += off;
        span->u0 += FixedMul(ustep, off);
        span->v0 += FixedMul(vstep, off);
        span->w0 += FixedMul(wstep, off);
    }
    else /* "subtexel" correction */
    {
        off = FIXED_ONE - (span->x0 & 0x0000ffff);
        span->x0 &= 0xffff0000;
        span->u0 += FixedMul(ustep, off);
        span->v0 += FixedMul(vstep, off);
        span->w0 += FixedMul(wstep, off);
    }
    off = span->x1 - cx1;
    if(off > 0)
    {
        span->x1 -= off;
        span->u1 -= FixedMul(ustep, off);
        span->v1 -= FixedMul(vstep, off);
        span->w1 -= FixedMul(wstep, off);
    }
    span->x1 &= 0xffff0000;
    return 1;
}

int __cdecl ClipSpanUV(const RasterRect *clip, int *x0, int *x1, RasterPosUV *uv0, RasterPosUV *uv1)
{
    Fixed invw = FIXED_ONE / (*x1 - *x0);
    Fixed ustep = FixedMul((uv1->u - uv0->u), invw);
    Fixed vstep = FixedMul((uv1->v - uv0->v), invw);
    if(*x1 < clip->x || *x0 > (clip->x + clip->w))
    {
        *x0 = 0; *x1 = 0;
        return 0;
    }
    if(*x1 > (clip->x + clip->w))
    {
        int off = *x1 - (clip->x + clip->w);
        uv1->u -= ustep * off;
        uv1->v -= vstep * off;
        *x1 -= off;
    }
    if(*x0 < clip->x)
    {
        int off = clip->x - *x0;
        uv0->u += ustep * off;
        uv0->v += vstep * off;
        *x0 += off;
    }

    return 1;
}

static void __cdecl _SpansBetween(Raster *raster, Edge *edge0, Edge *edge1, SpanFn fn, void *args)
{
    int stopy = raster->clipRect.y + raster->clipRect.h;
    int bottom = edge0->y + IMIN(edge0->h, edge1->h);
    int fnret = 0;

    if(edge0->y != edge1->y)
        return; /* can't */
    if(edge0->h == 0 || edge1->h == 0)
        return; /* nothing to do */

    stopy = IMIN(stopy, bottom);

    /* vertical clipping */
    if(edge0->y < raster->clipRect.y)
    {
        EdgeSkip(edge0, raster->clipRect.y - edge0->y);
        EdgeSkip(edge1, raster->clipRect.y - edge1->y);
    }
    if(edge0->y >= stopy)
        return; /* nothing to do */

    do
    {
        fnret = fn(raster, edge0, edge1, args);
        EdgeCrawl(edge0);
        EdgeCrawl(edge1);
    } while(fnret == 0 && edge0->y < stopy);
}

static void __cdecl _SpansBetweenChooser(Raster *raster, Edge *edge0, Edge *edge1, SpanFn fn, void *args);

void (__cdecl * SpansBetween)(Raster *raster, Edge *edge0, Edge *edge1, SpanFn fn, void *args) = _SpansBetweenChooser;

static void __cdecl _SpansBetweenChooser(Raster *raster, Edge *edge0, Edge *edge1, SpanFn fn, void *args)
{
    SpansBetween = _SpansBetween;
    SpansBetween(raster, edge0, edge1, fn, args);
}
